<?php
namespace app\controllers\crontab;
use Yii;
use app\models\marketing\MailGroup;
use app\components\redis\RedisMarketingEmail;
use app\components\redis\RedisLock;
use app\models\Elog;
use app\models\marketing\MailGroupEmailTypeStatistics;
use app\models\marketing\MailSendlist;
use app\components\Site;
use app\models\marketing\SendlistUseridTemp;
use Ares333\Curl\Toolkit;

/**
 * 用户分群 , 每分钟构建分组的邮件用户信息
 * @author xqg <xiaoqingguang@globalegrow.com>
 * @date 2017-12-12
 * 
 */
class AddUserQueueController extends \app\base\Controller 
{
    
    /**
     * 监控从es中获取数据的分组是否
     */
    public function actionMonitorGroup() 
    {
        ignore_user_abort(true);     // 忽略用户的中断
        set_time_limit(0);
        $method = yii::$app->request->getMethod();
        $formData = yii::$app->request->$method();
        $siteName = $formData['deal_site_name']; //  需要处理的网站名
        $redisKey = "{$siteName}:getesdata:group"; // 保存从es中获取的当前分组用户总数
        $redisKeyLastTime = "{$siteName}:getesdata:group:lasttime"; // 记录当前分组从es的最后一次获取用户数据时间
        // 获取 $redisKey所有值
        $redis = new RedisMarketingEmail();
       
       
        $redisLock = new RedisLock($redis, "deal_group_to_sendlist_monitor_group_{$siteName}", 3600);
         if ($redisLock->translock() === false) {
            $msg = "当前网站已经有一个监控构建的进程在跑";
            Elog::logCrontab($msg,__FUNCTION__,'info');
            return 0;
        }
        try {
            $allGroupInfo = $redis->hgetall($redisKey);
            $allGroupLastTime = $redis->hgetall($redisKeyLastTime);
            // 遍历相关数据
            $successGroup = [];// 可以处理的分组
            $badGroup = []; // 处理失败的分组
            $waitGroup = []; // 还没有处理完的分组
            $allGroup = [];
            $allGroupCount = [];
            $allGroupLastTimeDeal = [];
            $curTime = time(); 
            foreach ($allGroupInfo as $key => $allGroupInfoVal) {
                if ($key % 2 == 0) {
                    $allGroup[] = $allGroupInfoVal;
                    $allGroupCount[$allGroupInfoVal] = $allGroupInfo[$key+1];
                } 
            }
            foreach ($allGroupLastTime as  $key => $allGroupLastTimeVal ) {
                if ($key % 2 == 0) {
                    $allGroupLastTimeDeal[$allGroupLastTimeVal] = $allGroupLastTime[$key+1];
                } 
            }
            $sendlistUseridTemp = new SendlistUseridTemp();
            $databaseGroupInfo = $sendlistUseridTemp->getGroupCount($siteName,$allGroup);
            $databaseGroupInfoDeal = [];

            foreach ($databaseGroupInfo as $databaseGroupInfoVal) {
                $databaseGroupInfoDeal[$databaseGroupInfoVal['group_id']] = $databaseGroupInfoVal['count'];
            }

            foreach ($allGroup as $allGroupVal) {

                // 如果数据库中实际保存数据条数等于redis中值则说明成功
                if (isset($databaseGroupInfoDeal[$allGroupVal])) {
                    if ($databaseGroupInfoDeal[$allGroupVal] == $allGroupCount[$allGroupVal]) { // 说明成功
                        $successGroup[] = $allGroupVal;
                    } elseif ($databaseGroupInfoDeal[$allGroupVal] > $allGroupCount[$allGroupVal]) { //实际获取的总数大于redis中保存的总数说明构建有问题需要重新构建
                        $badGroup[] = $allGroupVal;
                    } else {
                        if (empty($allGroupLastTimeDeal[$allGroupVal]) || $allGroupLastTimeDeal[$allGroupVal] < $curTime-600) { // 如果10分钟没有数据变动并且总数小于redis中保存值则需要重新构建
                            $badGroup[] = $allGroupVal;  
                        } 
                    }
                } else {
                    $badGroup[] = $allGroupVal;
                }
            }

            echo "数据完整的分组有:".join(',',$successGroup);
            echo '<br>--------------------------------<br>';
            echo '重新构建的分组有:'.join(',',$badGroup);
            if (!empty($badGroup) || !empty($successGroup)) {
                $toolkit = new Toolkit();
                $curl = $toolkit->getCurl();
                $curl->onInfo = null;

                // 成功的分组处理
                foreach ($successGroup as $successGroupId) {
                    $curl->add(
                        array(
                            'opt' => array(
                                CURLOPT_URL => "http://{$_SERVER['HTTP_HOST']}/crontab/add-user-queue/deal-success-group?site=marketing_email&deal_site_name={$siteName}&group_id={$successGroupId}",
                            )
                        ),
                        function ($r) {
                            echo "Request success for " . $r['info']['url'] . "\n";
                            echo $r['body']."\n";
                        }); 
                }
                
                // 失败分组处理
                foreach ($badGroup as $badGroupId) {
                    // 删除临时表中相关数据
                    $sendlistUseridTemp->delUserByGroupId($siteName,$badGroupId);
                    $curl->add(
                        array(
                            'opt' => array(
                                CURLOPT_URL => "http://{$_SERVER['HTTP_HOST']}/crontab/add-user-queue/re-adduserqueue?site=marketing_email&id={$badGroupId}",
                            )
                        ),
                        function ($r) {
                            echo "Request success for " . $r['info']['url'] . "\n";
                            echo $r['body']."\n";
                        }); 
                    // 把redis中相关数据删除掉
                    // 删除redis中相关数据
                    $redis->hdel($redisKey,$badGroupId);
                    $redis->hdel($redisKeyLastTime,$badGroupId); 
                }

                // 有问题的分组处理
                $curl->start();
            }
        } catch (\Exception $ex) {
            // 接收异常
            $msg=__FUNCTION__." es版构建监控进程异常：".$ex->getMessage().' ';

            // 记录入elog
            Elog::logCrontab($msg, __FUNCTION__ ,'exception');
            echo $msg;
        }
        $redisLock->unlock();
    }
    
    /**
     * 从es中已经获取了完整数据的分组处理
     */
    public function actionDealSuccessGroup()
    {
        ignore_user_abort(true);     // 忽略用户的中断
        set_time_limit(0);
        $method = yii::$app->request->getMethod();
        $formData = yii::$app->request->$method();
        $siteName = $formData['deal_site_name']; //  需要处理的网站名
        $groupId = intval($formData['group_id']);
        
        $redis = new RedisMarketingEmail();
        $redisLock = new RedisLock($redis, "deal_group_to_sendlist_{$siteName}_{$groupId}", 3600); // 保证一个分组只有一个进程在处理
         if ($redisLock->translock() === false) {
            $msg = "网站id为： {$siteId} 分组id为：{$groupId}的分组在获取数据当中";
            Elog::logCrontab($msg,__FUNCTION__,'info');
            return 0;
        }
        $redisKey = "{$siteName}:getesdata:group"; // 已经生成分组总数的相关数据
        $redisKeyLastTime = "{$siteName}:getesdata:group:lasttime"; // 记录当前分组用户的最后一次更新时间
        $sendlistUseridTemp = new SendlistUseridTemp();
        $mailGroup = new MailGroup();
        $groupInfo = $mailGroup->getGroupById($groupId);
        if ($groupInfo['is_build_data'] == 0) // 如果当前分组没有构建完则构建
        {
            $startime = time();
            $mailSendlist = new MailSendlist();
            $trans = Yii::$app->db->beginTransaction();
            try {
                $count = $mailSendlist->addUserToSendListEs($siteName,$groupId,$groupInfo['channel_id'],$groupInfo['template_pk']);
          
                if (0 == $count) {
                    // 更新为删除状态
                    $mailGroup->updateGroupByCondition(['group_id' => $groupInfo['group_id']], ['is_del' => 1]);
                } else {
                    $statModel = new MailGroupEmailTypeStatistics();
                    // 更新分组总数/构建
                    $last_data_time  = time() - $startime;
                    $mailGroup->updateGroupByCondition(['group_id' => $groupInfo['group_id']], ['total_count' => $count,'is_build_data'=>1,'spend_time'=>$last_data_time]);
                    
                    if(isset($groupInfo['is_ab_test']) && $groupInfo['is_ab_test'] == 1 && isset($groupInfo['ab_test_parent_id']) && $groupInfo['ab_test_parent_id'] == 0) {
                        // 获取对应的ab测试子分组信息
                        $getSql = "select g.group_id,t.* from mail_group g join mail_test_child_group t on g.ab_test_real_id = t.id where g.ab_test_parent_id = ".$groupInfo['group_id']." and g.is_ab_test=1 ";
                        $testGroups = Yii::$app->db->createCommand($getSql)->queryAll();
                        
                        foreach($testGroups as $testGroup) {
                            $testGroupNums = round($count*$testGroup['send_users_percent']/100);

                            $curCount = $sendListModel->updateSendlistTestGroupId($groupInfo['site_id'], $testGroupNums, $testGroup['group_id'], $groupInfo['group_id']);

                            if (0 == $curCount) {
                                // 更新为删除状态
                                $mailGroup->updateGroupByCondition(['group_id' => $testGroup['group_id']], ['is_del' => 1]);
                            } else {
                                // 更新分组总数/构建
                                $mailGroup->updateGroupByCondition(['group_id' => $testGroup['group_id']], ['total_count' => $curCount,'is_build_data'=>1,'spend_time'=>time() - $startime]);
                                // 更新报表统计
                                $statModel->addMailStatItem($testGroup['group_id'], $groupInfo['site_id']);
                            }
                        }
                    } else {
                        // 更新报表统计
                        $statModel->addMailStatItem($groupInfo['group_id'], $groupInfo['site_id']);
                    }
                }
                
                // 删除redis中相关数据
                $redis->hdel($redisKey,$groupId);
                $redis->hdel($redisKeyLastTime,$groupId);
                $trans->commit();
            } catch (Exception $ex) {
                $trans->rollBack();
                // 接收异常
                $msg=__FUNCTION__." es版数据获取成功的分组插入到队列表异常：".$ex->getMessage().' ';

                // 记录入elog
                Elog::logCrontab($msg, __FUNCTION__ ,'exception');
                echo $msg;
            }   
        }
        // 正常结束-释放锁
        $redisLock->unlock();
    }
    /**
     * 构建邮件发送队列用户数据
     * 
     * @example /crontab/add-user-queue/adduserqueue?site=marketing_email 访问地址
     */
    public function actionAdduserqueue() {
        
        // 忽略用户的中断
        ignore_user_abort(true);     
        
        // 设置脚本不超时
        set_time_limit(0);
        
        // 实例化分组对象
        $groupModel = new MailGroup();
        
        // 实例化营销REDIS对象
        $redis = new RedisMarketingEmail();
        
        // 实例化邮件发送记录
        $sendListModel = new MailSendlist();
        
        // 实例化报表统计对象
        $statModel = new MailGroupEmailTypeStatistics();
        $method = yii::$app->request->getMethod();
        $formData = yii::$app->request->$method();
        
        $siteId = 0;
        if (!empty($formData['deal_site_name'])) {
            //实例化REDIS锁对象, 同一网站两次同步数据请求间隔需大于3600s
            $redisLock = new RedisLock($redis, "actionAdduserqueue{$formData['deal_site_name']}", 900);

            // REDIS队列KEY
            $redisKey="addUserGroupSendList:{$formData['deal_site_name']}";
            
            $siteId = isset(yii::$app->params['sites']['marketing_email']['web_config'][$formData['deal_site_name']]['site_id']) ?
                    yii::$app->params['sites']['marketing_email']['web_config'][$formData['deal_site_name']]['site_id'] : 0;
        } else {
            $redisLock = new RedisLock($redis, "actionAdduserqueue", 3600);
            $redisKey='addUserGroupSendList';
        }
        $startime = time();
        try {
            
            // 获取没有构建好的分组数据写回到redis中
            $siteName = $formData['deal_site_name'];
            $siteInfo = yii::$app->params['sites']['marketing_email']['web_config'][$siteName]; 
            if (empty($siteInfo['accessEs'])) {
                if ($redisLock->translock() === false) {
                    // 10s存在重复消费队列
                    $msg = 'the process fail to get the lock,please wait!';
                    Elog::logCrontab($msg, 'actionAdduserqueue','info');
                    echo '有请求在处理中，拒绝该请求！';
                    exit;
                }
            }
            
            // 获取待处理队列内容
            $redisData = $redis->rpop($redisKey);
            if(empty($redisData)) {


                // 获取没有构建好的分组数据写回到redis中
                $siteName = $formData['deal_site_name'];
                $siteInfo = yii::$app->params['sites']['marketing_email']['web_config'][$siteName]; 

                if (empty($siteInfo['accessEs'])) {
                    $allNoBuildGroups = $groupModel->getNoBuildGroups($siteId);
                } else {
                    $allNoBuildGroups = []; // 开启es模式的有自己的重构机制，不在这一步加入重构
                }
                $noBuildGroupIds = [];
                foreach($allNoBuildGroups as $allNoBuildGroup) {
                    $noBuildGroupIds[] = $allNoBuildGroup['group_id'];
                }
                $sendlistGroupIds = [];
                if($noBuildGroupIds) {
                    $sendlistGroupInfo = $sendListModel->getCountsByGroupIds($siteId, $noBuildGroupIds);
                    
                    foreach($sendlistGroupInfo as $sendlistGroup) {
                        $sendlistGroupIds[] = $sendlistGroup['group_id'];
                    }
                }
                
                if($allNoBuildGroups) {
                    foreach($allNoBuildGroups as $allNoBuildGroup) {
                        if(!in_array($allNoBuildGroup['group_id'], $sendlistGroupIds)) {
                            $redis->lpush($redisKey, $allNoBuildGroup['condition_log']);
                        }
                    }
                     echo 'ReAddNoBuildGroupsToRedis actionAddUserqueue Queue';
                } else {
                    // 队列没有内容
                    echo 'Empty actionAddUserqueue Queue';
                }
                
                // 正常结束-释放锁
                $redisLock->unlock();
                
                exit;
            }
            
            // 解码
            $redisData = json_decode($redisData, true);
            $count = 0;
            
            if(isset($redisData['is_ab_test']) && $redisData['is_ab_test'] == 1 && isset($redisData['ab_test_parent_id']) && $redisData['ab_test_parent_id'] > 1) { // 子分组构建相关
                // 通过分组ID，获取记录
                $groupData = $groupModel->getGroupById($redisData['ab_test_parent_id']);
                // 没有找到分组数据
                if(empty($groupData)) {

                    // 输出日志
                    $msg = 'Not found group data;group_id='.$groupData['group_id'];
                    Elog::logCrontab($msg, 'actionAddUserqueue','error');

                    // 正常结束-释放锁
                    $redisLock->unlock();

                    echo $msg;
                    exit;
                }
                // 内容已经生成过
                if($groupData['total_count'] == 0 || $groupData['is_build_data'] == 0) {

                    // 输出日志
                    $msg = 'Exist group  data , group_id='.$groupData['group_id'];
                    Elog::logCrontab($msg, 'actionAddUserqueue','info');

                    // 正常结束-释放锁
                    $redisLock->unlock();

                    echo $msg;
                    exit;
                }
                $count = $groupData['total_count'];
                // 通过分组ID，获取记录
                $groupData = $groupModel->getGroupById($redisData['group_id']);
                // 没有找到分组数据
                if(empty($groupData)) {

                    // 输出日志
                    $msg = 'Not found group data;group_id='.$redisData['group_id'];
                    Elog::logCrontab($msg, 'actionAddUserqueue','error');

                    // 正常结束-释放锁
                    $redisLock->unlock();

                    echo $msg;
                    exit;
                }
                // 内容已经生成过
                if($groupData['total_count']>0 || $groupData['is_build_data'] == 1) {

                    // 输出日志
                    $msg = 'Exist group  data , group_id='.$groupData['group_id'];
                    Elog::logCrontab($msg, 'actionAddUserqueue','info');

                    // 正常结束-释放锁
                    $redisLock->unlock();

                    echo $msg;
                    exit;
                }
                
                // 获取对应的ab测试子分组信息
                $getSql = "select g.group_id,t.* from mail_group g join mail_test_child_group t on g.ab_test_real_id = t.id where g.group_id = ".$groupData['group_id']." and g.is_ab_test=1 ";
                $testGroup = Yii::$app->db->createCommand($getSql)->queryOne();
                
                $testGroupNums = round($count*$testGroup['send_users_percent']/100);
                
                $trans = Yii::$app->db->beginTransaction();
                try{
                
                    $curCount = $sendListModel->updateSendlistTestGroupId($groupData['site_id'], $testGroupNums, $testGroup['group_id'], $redisData['ab_test_parent_id']);

                    if (0 == $curCount) {
                        // 更新为删除状态
                        $groupModel->updateGroupByCondition(['group_id' => $testGroup['group_id']], ['is_del' => 1]);
                    } else {
                        // 更新分组总数/构建
                        $groupModel->updateGroupByCondition(['group_id' => $testGroup['group_id']], ['total_count' => $curCount,'is_build_data'=>1,'spend_time'=>time() - $startime]);
                        // 更新报表统计
                        $statModel->addMailStatItem($testGroup['group_id'], $groupData['site_id']);
                    }
                    $trans->commit();   
                } catch (\Exception $ex) {
                    
                    $trans->rollBack();
                    // 接收异常
                    $msg='actionAddUserqueue Exception :'.$ex->getMessage().' ';

                    // 记录入elog
                    Elog::logCrontab($msg, 'actionAddUserqueue','exception');
                    echo $msg;
                }
            } else {
            
                // 通过分组ID，获取记录
                $groupData = $groupModel->getGroupById($redisData['group_id']);

                // 没有找到分组数据
                if(empty($groupData)) {

                    // 输出日志
                    $msg = 'Not found group data;group_id='.$redisData['group_id'];
                    Elog::logCrontab($msg, 'actionAddUserqueue','error');

                    // 正常结束-释放锁
                    $redisLock->unlock();

                    echo $msg;
                    exit;
                }

                // 内容已经生成过
                if($groupData['total_count']>0) {

                    // 输出日志
                    $msg = 'Exist group  data , group_id='.$groupData['group_id'];
                    Elog::logCrontab($msg, 'actionAddUserqueue','info');

                    // 正常结束-释放锁
                    $redisLock->unlock();

                    echo $msg;
                    exit;
                }
                $siteName = Site::getInstance()->getSiteShortNameById($redisData['site_id']);
                $siteInfo = yii::$app->params['sites']['marketing_email']['web_config'][$siteName];   
                if (!empty($siteInfo['accessEs']) && !empty($redisData['esAllSelectInfo'])) {
                    try {
                        $sendListModel->addSendListByUserGroupEs($redisData['site_id'], 
                                                                $redisData['group_id'], 
                                                                $redisData['esAllSelectInfo']);
                        return; // es构建不进行下一步操作
                    } catch (\Exception $ex) {
                        // $trans->rollBack();
                       
                        // 接收异常
                        $msg=__FUNCTION__.' Exception 构建时从es获取数据到临时表步骤出现问题:'.$ex->getMessage().' ';

                        // 记录入elog
                        Elog::logCrontab($msg, 'actionAddUserqueue','exception');

                        echo $msg;
                        return;
                    }   
                } else {
                    $trans = Yii::$app->db->beginTransaction();
                    try{
                       // 增加发送队列
                        $siteName = Site::getInstance()->getSiteShortNameById($redisData['site_id']);
                        $siteInfo = yii::$app->params['sites']['marketing_email']['web_config'][$siteName]; 


                            $count = $sendListModel->addSendListByUserGroup($redisData['site_id'], 
                                                                    $redisData['group_id'], 
                                                                    $redisData['channel_id'],
                                                                    $redisData['template_pk'],
                                                                    $redisData['allSelectInfo'],
                                                                    1);

                            $last_data_time  = time() - $startime;
                            if (0 == $count) {
                                // 更新为删除状态
                                $groupModel->updateGroupByCondition(['group_id' => $redisData['group_id']], ['is_del' => 1]);
                            } else {
                                // 更新分组总数/构建
                                $groupModel->updateGroupByCondition(['group_id' => $redisData['group_id']], ['total_count' => $count,'is_build_data'=>1,'spend_time'=>$last_data_time]);

                                if(isset($redisData['is_ab_test']) && $redisData['is_ab_test'] == 1 && isset($redisData['ab_test_parent_id']) && $redisData['ab_test_parent_id'] == 0) {
                                    // 获取对应的ab测试子分组信息
                                    $getSql = "select g.group_id,t.* from mail_group g join mail_test_child_group t on g.ab_test_real_id = t.id where g.ab_test_parent_id = ".$groupData['group_id']." and g.is_ab_test=1 ";
                                    $testGroups = Yii::$app->db->createCommand($getSql)->queryAll();

                                    foreach($testGroups as $testGroup) {
                                        $testGroupNums = round($count*$testGroup['send_users_percent']/100);

                                        $curCount = $sendListModel->updateSendlistTestGroupId($groupData['site_id'], $testGroupNums, $testGroup['group_id'], $groupData['group_id']);

                                        if (0 == $curCount) {
                                            // 更新为删除状态
                                            $groupModel->updateGroupByCondition(['group_id' => $testGroup['group_id']], ['is_del' => 1]);
                                        } else {
                                            // 更新分组总数/构建
                                            $groupModel->updateGroupByCondition(['group_id' => $testGroup['group_id']], ['total_count' => $curCount,'is_build_data'=>1,'spend_time'=>time() - $startime]);
                                            // 更新报表统计
                                            $statModel->addMailStatItem($testGroup['group_id'], $groupData['site_id']);
                                        }
                                    }
                                } else {
                                    // 更新报表统计
                                    $statModel->addMailStatItem($redisData['group_id'], $redisData['site_id']);
                                }
                            }

                            $trans->commit();   

                    } catch (\Exception $ex) {

                        $trans->rollBack();
                        // 接收异常
                        $msg='actionAddUserqueue Exception :'.$ex->getMessage().' ';

                        // 记录入elog
                        Elog::logCrontab($msg, 'actionAddUserqueue','exception');

                        echo $msg;
                    }
                }
                
            }
            $last_time  = time() - $startime;
            if( $last_time < 60 ){
                $dif_t = $last_time ."秒";
            }else{
                $dif_t = floor($last_time/60) . "分钟".$last_time%60 . "秒";
            }

            $siteName = Site::getInstance()->getSiteShortNameById($redisData['site_id']);
            $msg = $siteName." group_id ".$redisData['group_id']."本次构建邮件".$count."条，花费时间".$dif_t;
            Elog::logCrontab($msg, 'time','info');

            // 正常结束-释放锁
            $redisLock->unlock();
            
            echo 'END';
            exit;
        } catch (Exception $ex) {
            
            // 接收异常
            $msg='actionAddUserqueue Exception :'.$ex->getMessage().' ';
            
            // 记录入elog
            Elog::logCrontab($msg, 'actionAddUserqueue','exception');
            
            // 异常结束-释放锁
            if($redisLock) {
                $redisLock->unlock();
            }
        }
        
    }
    
    /**
     * 通过GROUP_ID重新入队列,异常队列重发
     * 
     * @example /crontab/add-user-queue/re-adduserqueue?site=marketing_email 访问地址
     */
    public function actionReAdduserqueue() {
        
        // 获取分组ID
        $groupId = intval(Yii::$app->request->getQueryParam('id'));
        
        if($groupId<=0) {
            // 不满足的groupId
            echo '参数传入错误';
            exit;
        }
        
        // 实例化分组对象
        $groupModel = new MailGroup();
        
        // 通过分组ID，获取记录
        $groupData = $groupModel->getGroupQueueById($groupId);
        
        if(empty($groupData)) {
            // 没有找到分组ID
            echo 'Not found the group data';
            exit;
        }
        
        // 实例化营销REDIS对象
        $redis = new RedisMarketingEmail();
        
        // 重写入队列
        $result = 0;
        if(!empty($groupData)) {
            
            // 获取网站名
            $groupInfoArr = \GuzzleHttp\json_decode($groupData['condition_log'],true);
            $siteName = Site::getInstance()->getSiteShortNameById($groupInfoArr['site_id']);
            // 加入队列
            $result = $redis->lpush("addUserGroupSendList:{$siteName}", $groupData['condition_log']);
        }
        
        if($result>0) {
            $msg = 'ok,id='.$result;
        }else {
            $msg = 'fail,id='.$result;
        }
        
        // 记录入elog
        Elog::logCrontab($msg, 'actionReAdduserqueue','info');
        
        echo $msg;
    }
}
